package com.dao;

import com.domain.Car;
import org.orm.hsc.Configuration;
import org.orm.hsc.JdbcExecutor;
import org.orm.hsc.SqlSession;
import org.orm.hsc.util.SqlSessionFactoryUtil;

import java.sql.*;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

/**
 * @author
 * @date 2025-04-09
 * @Description 模拟原生Jdbc的操作
 */
public class CarDao {

    /**
     * 保存一个car对象 通过对象传递参数
     * 使用最原生的方式jdbc方式进行保存数据库数据
     * 保存一个 获得一个主键自增值
     *
     * @param car
     */
    public void save(Car car) {
        //将car对象中的数据存入数据库
        String sql = "insert into t_car(cname,color,price) values(?,?,?)";
        Connection conn = null;
        PreparedStatement preparedStatement = null;
        ResultSet resultSet = null;
        try {
            //1. 引入驱动的jar文件
            //2. 加载驱动 mysql8.jar之后建议使用的驱动 带cj目录
//            Class.forName("com.mysql.cj.jdbc.Driver");
            Class.forName("com.mysql.jdbc.Driver");
            //3. 获取连接 mysql8.0.1x url中需要指定&serverTimezone=utc
//            conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/test?characterEncoding=utf8", "root", "Admin@1234");
            conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/test?characterEncoding=utf8&useSSL=false", "root", "Admin@1234");
            //4. 创建执行器 处理sql，设置参数，传递sql（执行sql） 在创建statement对象时，传递一个参数，表示要获得自增主键值。
            preparedStatement = conn.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS);
            //4.5 设置参数 占位符 替代为实际的值
            preparedStatement.setString(1, car.getCname());
            preparedStatement.setString(2, car.getColor());
            preparedStatement.setDouble(3, car.getPrice());
            //5. 执行sql
            int count = preparedStatement.executeUpdate();
            //5.5 获取结果集
            resultSet = preparedStatement.getGeneratedKeys();
            resultSet.next();
            long cno = resultSet.getLong(1);
            System.out.println("本次自增值为：" + cno);
            //6. 关闭资源 stmt， conn ，无论成功与否，都需要关闭，所以应该在finally中关闭
        } catch (Exception e) {
            throw new RuntimeException(e);
        } finally {
            // 关闭各种资源
            if (conn != null) {
                try {
                    conn.close();
                } catch (SQLException e) {
                    throw new RuntimeException(e);
                }
            }
            if (preparedStatement != null) {
                try {
                    preparedStatement.close();
                } catch (SQLException e) {
                    throw new RuntimeException(e);
                }
            }
            if (resultSet != null) {
                try {
                    resultSet.close();
                } catch (SQLException e) {
                    throw new RuntimeException(e);
                }
            }
        }

    }

    /**
     * 版本一：通过写固定的sql 传递参数值
     * 保存多条数据  并且获得多个主键自增值
     */
    public void saves() {
        //将car对象中的数据存入数据库
        String sql = "insert into t_car(cname,color,price) values('byd1','black',150000),('byd2','black',160000),('byd3','black',170000)";
        //通过jdbc将sql命令传递给数据库服务器执行
        Connection conn = null;
        PreparedStatement preparedStatement = null;
        ResultSet resultSet = null;
        //1. 引入驱动的jar文件 。 尽量确保数据库版本和驱动版本一致。 也可以不一致需要额外的配置
        try {
            //2. 加载驱动 , mysql8.jar之后建议使用的驱动 带cj目录

//            Class.forName("com.mysql.cj.jdbc.Driver");
            Class.forName("com.mysql.jdbc.Driver");
            //3. 获取连接 mysql8.0.1x url中需要指定&serverTimezone=utc
//            conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/test?characterEncoding=utf8", "root", "Admin@1234");
            conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/test?characterEncoding=utf8&useSSL=false", "root", "Admin@1234");
            // mysql 8版本
//            conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/test?characterEncoding=utf8", "root", "Admin@1234");
            //4 创建命令集对象 ， 处理sql，设置参数，传递sql（执行sql）
            //stmt = conn.prepareStatement(sql) ; 在创建statement对象时，传递一个参数，表示要获得自增主键值。
            preparedStatement = conn.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS);
            //5 执行sql（传递sql）
            int count = preparedStatement.executeUpdate();
            System.out.println("本次插入了" + count + "条数据");
            //5.5 获得自增主键值

            resultSet = preparedStatement.getGeneratedKeys();
            //理论上这个结果集中有3条记录， 每条记录只有1个字段
            while (resultSet.next()) {
                long cno = resultSet.getLong(1);
                System.out.println("本次自增值为：" + cno);
            }
            //6 各种关闭 stmt， conn ，无论成功与否，都需要关闭，所以应该在finally中关闭
            System.out.println("本次插入了" + count + "条数据");
        } catch (Exception e) {
            throw new RuntimeException(e);
        } finally {
            if (conn != null) {
                try {
                    conn.close();
                } catch (SQLException e) {
                    throw new RuntimeException(e);
                }
            }
            if (preparedStatement != null) {
                try {
                    preparedStatement.close();
                } catch (SQLException e) {
                    throw new RuntimeException(e);
                }
            }
            if (resultSet != null) {
                try {
                    resultSet.close();
                } catch (SQLException e) {
                    throw new RuntimeException(e);
                }
            }
        }


    }

    /**
     * 版本二：保存多个数据 通过传递参数的方式 当需要添加多条数据时 我们需要自己控制事务手动提交
     * 如果多个语句执行过程中 有一个失败的 事务将会回滚 但是主键的自增值即使失败了 也还会自增
     */
    public void saves(List<Car> carList) {
        String sql = "insert into t_car(cname,color,price) values(?,?,?)";
        Connection conn = null;
        PreparedStatement preparedStatement = null;
        try {
            //1. 引入驱动的jar文件 。 尽量确保数据库版本和驱动版本一致。 也可以不一致需要额外的配置
            //2. 加载驱动 , mysql8.jar之后建议使用的驱动 带cj目录
//            Class.forName("com.mysql.cj.jdbc.Driver");
            Class.forName("com.mysql.jdbc.Driver");
            //3. 获取连接 mysql8.0.1x url中需要指定&serverTimezone=utc
//            conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/test?characterEncoding=utf8", "root", "Admin@1234");
            conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/test?characterEncoding=utf8&useSSL=false", "root", "Admin@1234");
            // 设置事务手动提交
            conn.setAutoCommit(false);
            for (Car car : carList) {
                //4 创建命令集对象 ， 处理sql，设置参数，传递sql（执行sql）
                //stmt = conn.prepareStatement(sql) ;
                preparedStatement = conn.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS);
                preparedStatement.setString(1, car.getCname());
                preparedStatement.setString(2, car.getColor());
                preparedStatement.setDouble(3, car.getPrice());
                //5 执行sql（传递sql）
                preparedStatement.executeUpdate();
                // 这里是更新语句，所以需要获取自增主键的值 一次一般只有一个
                //5.5 获得自增主键值
                ResultSet resultSet = preparedStatement.getGeneratedKeys();
                resultSet.next();
                long cno = resultSet.getLong(1);
                System.out.println("本次自增值为：" + cno);
            }
            // 表示保存完毕，并且没有出现异常 提交事务
            conn.commit();

        } catch (ClassNotFoundException e) {
            throw new RuntimeException(e);
        } catch (SQLException e) {
            e.printStackTrace();
            // 表示出现异常 回滚事务
            if (conn != null) {
                try {
                    conn.rollback();
                } catch (SQLException ex) {
                    throw new RuntimeException(ex);
                }
            }
            throw new RuntimeException(e);
        } finally {
            if (conn != null) {
                try {
                    conn.close();
                } catch (SQLException e) {
                    throw new RuntimeException(e);
                }
            }
            if (preparedStatement != null) {
                try {
                    preparedStatement.close();
                } catch (SQLException e) {
                    throw new RuntimeException(e);
                }
            }
        }

    }


    /**
     * 原始jdbc方式查询所有的数据 并且封装为集合返回
     *
     * @return
     */
    public List<Car> findAll() {
        //从数据库查询所有的汽车信息
        String sql = "select * from t_car";
        //1 引入驱动jar文件
        Connection conn = null;
        ResultSet resultSet = null;
        PreparedStatement preparedStatement = null;
        try {
            //2 加载驱动
//            Class.forName("com.mysql.cj.jdbc.Driver");
            Class.forName("com.mysql.jdbc.Driver");
            //3. 获取连接 mysql8.0.1x url中需要指定&serverTimezone=utc
//            conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/test?characterEncoding=utf8", "root", "Admin@1234");
            conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/test?characterEncoding=utf8&useSSL=false", "root", "Admin@1234");
            //4 创建命令集对象， 传递sql（执行sql）
            preparedStatement = conn.prepareStatement(sql);
            //5 执行sql
            resultSet = preparedStatement.executeQuery();
            //5.5 处理结果集, 将结果集中的数据（表数据），转换成java所识别的类型（List<Car>)
            List<Car> cars = new ArrayList<>();
            while (resultSet.next()) {
                Car car = new Car();
                car.setCno(resultSet.getLong("cno"));
                car.setCname(resultSet.getString("cname"));
                car.setColor(resultSet.getString("color"));
                car.setPrice(resultSet.getDouble("price"));
                //将car对象添加到list集合中
                cars.add(car);
            }
            //    这个过程就是orm过程（一部分）
            //6 各种关闭 rs , stmt , conn
            return cars;
        } catch (Exception e) {
            throw new RuntimeException(e);
        } finally {
            if (conn != null) {
                try {
                    conn.close();
                } catch (SQLException e) {
                    throw new RuntimeException(e);
                }
            }
            if (preparedStatement != null) {
                try {
                    preparedStatement.close();
                } catch (SQLException e) {
                    throw new RuntimeException(e);
                }
            }
            if (resultSet != null) {
                try {
                    resultSet.close();
                } catch (SQLException e) {
                    throw new RuntimeException(e);
                }
            }

        }

    }


    /**
     * 利用executor工具实现保存 单条数据
     */
    public void  saveByExecutor(Car car){
        //准备将数据存入数据库
        String sql = "insert into t_car(cname,color,price) values(?,?,?)" ;
        //原来自己写jdbc
        //现在使用executor工具
        JdbcExecutor executor = new JdbcExecutor(true);
        executor.doUpdate(sql,true,new Object[]{car.getCname(),car.getColor(),car.getPrice()});
        System.out.println("executor.getGeneratedKeys() = " + executor.getGeneratedKeys());
    }

    /**
     * 利用executor工具实现保存 多条数据
     * 通过Dao 层来手动提交事务
     */
    public void savesByExecutor(List<Car> cars) {
        // 创建Executor 工具 并且设置为手动提交事务
        JdbcExecutor jdbcExecutor = new JdbcExecutor(false);
        String sql="insert into t_car(cname,color,price) values(?,?,?)";
        for (Car car : cars) {
            // 遍历执行多次 控制为一个事务中处理 要么全部成功 要么全部失败
            jdbcExecutor.doUpdate(sql,true,new Object[]{car.getCname(),car.getColor(),car.getPrice()});
            System.out.println("jdbcExecutor.getGeneratedKeys() = " + jdbcExecutor.getGeneratedKeys());

        }
        // 手动提交事务
        jdbcExecutor.commit();
    }

    /**
     * 测试将返回的结果进行封装
     * @return
     */
    public int findCount() {
        String sql = "select count(*) from t_car" ;
        // JdbcExecutor 封装了Jdbc 相关操作
        JdbcExecutor jdbcExecutor = new JdbcExecutor();
        // 给定查询的返回值为Integer
        List<Integer> list =jdbcExecutor.doQuery(sql,Integer.class);
        return list.get(0);
    }

    /**
     * 测试数据的返回结果类型用Map封装
     * @return
     */
    public Map findMap() {
        String sql = "select count(*) , max(price) , min(price) from t_car " ;
        JdbcExecutor jdbcExecutor = new JdbcExecutor();
        List<Map> list = jdbcExecutor.doQuery(sql,Map.class);
        // 获取一行数据即可
        return list.get(0);
    }

    public List<Car> findDomain() {
        String sql = "select * from t_car" ;
        JdbcExecutor jdbcExecutor = new JdbcExecutor();
        List<Car> list = jdbcExecutor.doQuery(sql,Car.class);
        return list;
    }

    /**
     * 执行新规则的sql使用占位符方式：  insert into t_car(car_name,color,price) values(#{cname},#{color},#{price})
     * @param car
     */
    public void saveByNewSql(Car car) {
        //准备将数据存入数据库
        String sql = "insert into t_car(car_name,color,price) values(#{cname},#{color},#{price})" ;
        JdbcExecutor jdbcExecutor = new JdbcExecutor();
        // JdbcExecutor 复杂底层jdbc复杂的逻辑Dao 层负责使用
        jdbcExecutor.doUpdate(sql,true,car);
        // 获取主键值
        System.out.println("jdbcExecutor.getGeneratedKeys() = " + jdbcExecutor.getGeneratedKeys());

        // 手动提交事务
        jdbcExecutor.commit();
    }

    /**
     * 不在直接使用JdbcExecutor  操作数据库 通过 SqlSession 来操作数据库
     * 这里的sqlSession 只支持新规则的sql了 不支持旧规则的sql
     * @param car
     */
    public void saveBySession(Car car) {
        //准备将数据存入数据库
        String sql = "insert into t_car(car_name,color,price) values (#{cname},#{color},#{price})" ;
        //原来自己写jdbc
        //后来使用executor工具
        //现在使用SqlSession
        SqlSession sqlSession = new SqlSession();
        // 执行插入操作 并且将自增的主键值放在 我们给定字段中  cno
        sqlSession.insert(sql,car,"cno");
        // 手动提交事务
        sqlSession.commit();
        // 获取car对象的主键值
        System.out.println("car.getCno() = " + car.getCno());

    }

    /**
     * 使用SqlSession 并且封装到domain中
     * @return
     */
    public List<Car> findBySession() {
        String sql = "select * from t_car" ;
        SqlSession sqlSession = new SqlSession();
        return sqlSession.selectList(sql,Car.class);
    }

    public void saveByConfiguration(Car car) {
        //准备将数据存入数据库
        String sql = "insert into t_car(cname,color,price) values" +
                "(#{cname},#{color},#{price})" ;
        Configuration configuration = new Configuration("mysql-jdbc.properties");
        // 设置configuration 初始化  executor  conn
        SqlSession sqlSession=new SqlSession(configuration);
        System.out.println(car.getCno()); //无值
        sqlSession.insert(sql,car,"cno");
        sqlSession.commit();
        System.out.println(car.getCno()); //有值

    }

    public void saveByFactory(Car car) {
        //准备将数据存入数据库
        String sql = "insert into t_car(cname,color,price) values (#{cname},#{color},#{price})" ;
        // 通过 SqlSessionFactory创建SqlSession
        org.orm.hsc.session.SqlSession sqlSession = SqlSessionFactoryUtil.mysqlFactory.openSession();
        sqlSession.insert(sql,car,"cno");
        sqlSession.commit();
        System.out.println(car);
    }

    /**
     * 通过注解方式设置sql
     * @param car
     */
    public void saveByInterface(Car car) {
        // SqlSessionFactory 获取哦sqlsession
        org.orm.hsc.session.SqlSession sqlSession = SqlSessionFactoryUtil.mysqlFactory.openSession(true);
        // 通过sqlId拿到对应的接口 进行反射着
        sqlSession.save("com.sql.CarSql.save",car);
    }

    /**
     * 通过注解方式查询数据 简化代码
     * @return
     */
    public List<Car> findAllByInterface() {
        org.orm.hsc.session.SqlSession sqlSession = SqlSessionFactoryUtil.mysqlFactory.openSession();
        // 通过配置文件 指定dao处理类
        List<Car> cars = sqlSession.searchList("com.sql.CarSql.findAll");
        return cars;
    }
}
